package com.cictec.middleware.tde.rm.protocol;

import java.nio.ByteBuffer;
import java.text.MessageFormat;
import java.util.Date;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.cictec.middleware.commons.utils.BinaryUtils;
import com.cictec.middleware.tde.exception.MessageEncoderException;
import com.cictec.middleware.tde.exception.NotFoundEncoderException;
import com.cictec.middleware.tde.rm.protocol.TerminalMessage.Header;
import com.cictec.middleware.tde.rm.protocol.rm.ProtocelConstants;
import com.cictec.middleware.tde.rm.protocol.rm.utils.RmBinaryUtils;

@Component
public class TerminalProtocolEncoder extends ProtocolEncoderAdapter {

	private static Logger logger = LoggerFactory.getLogger(TerminalProtocolEncoder.class);

	private int MAX_BODY_LENGTH = 242;

	@Autowired
	private MessageCodecFounder messageCodecFounder;

	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {

		TerminalMessage tm = (TerminalMessage) message;

		Header header = tm.getHeader();

		byte[] bodyBytes;

		try {
			bodyBytes = encodeBody(tm);
		} catch (NotFoundEncoderException cfe) {
			logger.error("找不到消息编码器  消息ID 0x{}", Integer.toHexString(header.getMessageId()));
			return;
		}

		header.setBodyLength(bodyBytes.length);

		byte[] headerBytes = encodeHeader(header);
		byte[] allBytes = translateFrameData(headerBytes, bodyBytes, header.isRealTimeData());

		logger.debug("发送消息 session {} 序号{} 消息ID 0x{} ： {} ", session.getId(), header.getMessageSequence(),
				Integer.toHexString(header.getMessageId()), BinaryUtils.byte2HexStr(allBytes));

		IoBuffer ioBuffer = IoBuffer.allocate(allBytes.length);
		ioBuffer.setAutoExpand(false);
		ioBuffer.setAutoShrink(true);
		ioBuffer.put(allBytes, 0, allBytes.length);
		ioBuffer.flip();
		// logger.debug("编码器 开始向 Session {} 发送数据",session.getId());

		out.write(ioBuffer);
		out.flush();

	}

	/**
	 * 组合完整的byte型的发送数据，包括附加校验码和字符转义。
	 * 
	 * @param header
	 * @param body
	 * @return
	 */
	public byte[] translateFrameData(byte[] header, byte[] body, boolean rt) {
		// 计算校验码
		int valiCode = BinaryUtils.validate(BinaryUtils.validate(header), body, 0, body.length);

		ByteBuffer buffer = ByteBuffer.allocate(512);
		// 写入消息开始标志
		buffer.put(BinaryUtils.intToUnsignedByte(ProtocelConstants.MESSAGE_BEGIN_FLAG));
		buffer.put(BinaryUtils.intToUnsignedByte(ProtocelConstants.MESSAGE_BEGIN_FLAG));
		// 数据总长度
		buffer.put(BinaryUtils.intToUnsignedByte(header.length + body.length + 1));

		buffer.put(header);
		buffer.put(body);

		// 写入是否为实时数据
		byte byteRt;
		if (rt) {
			byteRt = (byte) 1;
		} else {
			byteRt = (byte) 0;
		}
		buffer.put(byteRt);

		// 写入校验码
		valiCode = BinaryUtils.validate(valiCode, byteRt);
		byteEscape(buffer, BinaryUtils.intToUnsignedByte(valiCode));

		buffer.put(BinaryUtils.intToUnsignedByte(ProtocelConstants.MESSAGE_END_FLAG));
		buffer.put(BinaryUtils.intToUnsignedByte(ProtocelConstants.MESSAGE_END_FLAG));

		// 转换成byte数组
		byte[] bytes = new byte[buffer.position()];
		buffer.flip();
		buffer.get(bytes);
		return bytes;
	}

	private void byteEscape(ByteBuffer buffer, byte b) {
		int unsingnedInt = BinaryUtils.unsignedByteToInt(b);
		if (unsingnedInt == ProtocelConstants.MESSAGE_BEGIN_FLAG) {
			buffer.put(BinaryUtils.intToUnsignedByte(ProtocelConstants.MESSAGE_BEGIN_ESCAPE))
					.put(BinaryUtils.intToUnsignedByte(ProtocelConstants.MESSAGE_BEGIN_ESCAPE_BEGIN));
		} else if (unsingnedInt == ProtocelConstants.MESSAGE_BEGIN_ESCAPE) {
			buffer.put(b).put(BinaryUtils.intToUnsignedByte(ProtocelConstants.MESSAGE_BEGIN_ESCAPE_ESCAPE));
		} else {
			buffer.put(b);
		}
	}

	public byte[] encodeBody(TerminalMessage tm) throws NotFoundEncoderException, MessageEncoderException {

		MessageEncoder me = messageCodecFounder.getEncoder(tm.getHeader().getMessageId());

		if (me == null) {
			String hexId = Integer.toHexString(tm.getHeader().getMessageId());
			logger.error("找不到消息 0x{} 对应的编码器 ", hexId);
			throw new NotFoundEncoderException(MessageFormat.format("找不到消息 0x{0} 的附加消息 {1} 对应的编码器 ", hexId));
		}

		return me.encode(tm);
	}

	public byte[] encodeHeader(Header header) {
		// 分配消息头最大的占用空间。
		ByteBuffer headerBuffer = ByteBuffer.allocate(16);

		// 协议版本号
		String[] vers = header.getVersion().split("\\.");
		int verHigh = Integer.parseInt(vers[0]);
		int verLow = Integer.parseInt(vers[1]);
		int verNum = (verHigh << 4) | verLow;
		headerBuffer.put(BinaryUtils.intToUnsignedByte(verNum));
		// 消息流水号
		headerBuffer.put(BinaryUtils.intToUnsignedByte(header.getMessageSequence()));
		// 命令字
		headerBuffer.put(BinaryUtils.intToUnsignedByte(header.getMessageId()));
		// 发送时间
		if (header.getSendTime() == null)
			header.setSendTime(new Date());
		headerBuffer.put(RmBinaryUtils.dateToDwordRM(header.getSendTime()));
		// 终端编号
		byte[] terminalIdBytes = BinaryUtils.longToDword(header.getTerminalId());
		headerBuffer.put(terminalIdBytes, 1, 3);
		// 线路编号
		headerBuffer.put(BinaryUtils.intToWord(header.getLineId()));

		byte[] bytes = new byte[headerBuffer.position()];

		headerBuffer.limit(headerBuffer.position());
		headerBuffer.flip();

		headerBuffer.get(bytes);

		return bytes;
	}

}
